#include <osg/Camera>
#include <osg/Material>
#include <osg/ShapeDrawable>
#include <osg/StateSet>
#include <osgGA/EventQueue>
#include <osgGA/TrackballManipulator>
#include <osgViewer/Viewer>

#include <QApplication>
#include <QDesktopWidget>
#include <QMouseEvent>
#include <QWheelEvent>

#include "OpenSceneGraphWidget.h"

OpenSceneGraphWidget::OpenSceneGraphWidget(QWidget *parent)
    : QOpenGLWidget(parent)
    , m_graphicsWindow(new osgViewer::GraphicsWindowEmbedded(x(), y(), width(), height()))
    , m_viewer(new osgViewer::Viewer)
    , m_scale(QApplication::desktop()->devicePixelRatio())
{
    osg::ref_ptr<osg::Cylinder> cylinder = new osg::Cylinder(osg::Vec3(0.f, 0.f, 0.f), 0.25f, 0.5f);
    osg::ref_ptr<osg::ShapeDrawable> shape = new osg::ShapeDrawable(cylinder);
    shape->setColor(osg::Vec4(0.8f, 0.5f, 0.2f, 1.f));
    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(shape);

    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setViewport(0, 0, width(), height());
    camera->setClearColor(osg::Vec4(0.9f, 0.9f, 1.f, 1.f));
    float aspectRatio = static_cast<float>(width()) / static_cast<float>(height());
    camera->setProjectionMatrixAsPerspective(30.f, aspectRatio, 1.f, 1000.f);
    camera->setGraphicsContext(m_graphicsWindow);

    m_viewer->setCamera(camera);
    m_viewer->setSceneData(geode);
    osg::ref_ptr<osgGA::TrackballManipulator> manipulator = new osgGA::TrackballManipulator;
    manipulator->setAllowThrow(false);
    setMouseTracking(true);
    m_viewer->setCameraManipulator(manipulator);
    m_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
    m_viewer->realize();
}

OpenSceneGraphWidget::~OpenSceneGraphWidget()
{

}

void OpenSceneGraphWidget::initializeGL()
{
    osg::ref_ptr<osg::Geode> geode = dynamic_cast<osg::Geode *>(m_viewer->getSceneData());
    osg::ref_ptr<osg::StateSet> stateSet = geode->getOrCreateStateSet();
    osg::ref_ptr<osg::Material> material = new osg::Material;
    material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
    stateSet->setAttributeAndModes(material, osg::StateAttribute::ON);
    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
}

void OpenSceneGraphWidget::paintGL()
{
    m_viewer->frame();
}

void OpenSceneGraphWidget::resizeGL(int width, int height)
{
    getEventQueue()->windowResize(x() * m_scale, y() * m_scale, width * m_scale, height * m_scale);
    m_graphicsWindow->resized(x() * m_scale, y() * m_scale, width * m_scale, height * m_scale);
    osg::ref_ptr<osg::Camera> camera = m_viewer->getCamera();
    camera->setViewport(0, 0, this->width() * m_scale, this->height() * m_scale);
}

void OpenSceneGraphWidget::mouseMoveEvent(QMouseEvent *event)
{
    getEventQueue()->mouseMotion(event->x() * m_scale, event->y() * m_scale);
}

void OpenSceneGraphWidget::mousePressEvent(QMouseEvent *event)
{
    unsigned int button = 0;
    switch (event->button())
    {
    case Qt::LeftButton:
        button = 1;
        break;
    case Qt::MiddleButton:
        button = 2;
        break;
    case Qt::RightButton:
        button = 3;
        break;
    default:
        break;
    }
    getEventQueue()->mouseButtonPress(event->x() * m_scale, event->y() * m_scale, button);
}

void OpenSceneGraphWidget::mouseReleaseEvent(QMouseEvent *event)
{
    unsigned int button = 0;
    switch (event->button())
    {
    case Qt::LeftButton:
        button = 1;
        break;
    case Qt::MiddleButton:
        button = 2;
        break;
    case Qt::RightButton:
        button = 3;
        break;
    default:
        break;
    }
    getEventQueue()->mouseButtonRelease(event->x() * m_scale, event->y() * m_scale, button);
}

void OpenSceneGraphWidget::wheelEvent(QWheelEvent *event)
{
    int delta = event->angleDelta().y();
    osgGA::GUIEventAdapter::ScrollingMotion motion = delta > 0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN;
    getEventQueue()->mouseScroll(motion);
}

bool OpenSceneGraphWidget::event(QEvent *event)
{
    bool handled = QOpenGLWidget::event(event);
    update();
    return handled;
}

osgGA::EventQueue *OpenSceneGraphWidget::getEventQueue() const
{
    osg::ref_ptr<osgGA::EventQueue> eventQueue = m_graphicsWindow->getEventQueue();
    return eventQueue;
}

